#include "Commands.h"

void runCommand(){
    if(strcmp(command, "system_init") == 0){
        system_init();
    }
    else if(strcmp(command, "exit") == 0){
        exit(0);
    }
    else if(strcmp(command, "syscall") == 0){
        syscall();
    }
    else if(strcmp(command, "list") == 0){
        printf("Arg[0]: %S.\n", arg[0]);
        if(strcmp(arg[0], "lru")){
            listLRU();
        }
        else if(strcmp(arg[0], "queues")){
            printQueues();
        }
        listLRU();
    }
    else if(strcmp(command, "redirection") == 0){
        toggleRedirection();
    }
    else if(strcmp(command, "clear") == 0){
        clearScreen();
    }
    else if(strcmp(command, "go") == 0){
        process_control_block* pcb = go();
        if(pcb == 0){
            printf("Error: Ready Queue is empty or there's currently a running process.\n");
        }
        else{
            printf("Success! Moved PID %i into the running queue.\n", pcb->pid);
        }
    }
    else if(strcmp(command, "logical_dump") == 0){
        logicalDump(atoi(arg[0]));
		printf("\n");
    }
    else if(strcmp(command, "unwait") == 0){
        process_control_block* pcb;
        if(argc == 1){
            pcb = unwait(atoi(arg[0]));
        }
        else{
            printf("Usage: unwait {PID}\n");
        }
        if(pcb == 0){
            printf("Error: Specified PID is not in waiting queue.\n");
        }
        else{
            printf("Success! Moved PID %i into the ready queue.\n", pcb->pid);
        }
    }
    else if(strcmp(command, "*") == 0){
        if(buffer[0] == '*'){
            printf("*");
        }
        if(buffer[0] == '!'){
            printf("\n\n\n\n!");
        }
        if(buffer[0] != '\n'){
            printf("%s", comment);
        }
    }
    else{
        printf("Unknown command '%s'.\n", command);
    }
}


void system_init(){
	int mailboxes[10] = {1,1,1,1,1,1,1,1,1,1};
	/* Initialize the Process Manager with fair-share scheduling. */
    initializeProcessManager(1);
	printf("Process Manager initialized. \n");
	/* Initialize all 10 mailboxes. */
	initalizeMailboxes(mailboxes);
	printf("IPC Manager initialized. \n");
	/* Initialize the Memory Manager. */
	init_pagetable();
	printf("Memory Manager initialized. \n");
}


void syscall(){
    process_control_block* pcb;

    /* CREATE_P() */
    if(strcmp(arg[0], "create_p") == 0){
        if(argc != 1){
            printf("Usage: create_p\n");
            return;
        }
        create_p();

        /* If the create_p call worked, print the result. */
        if(error_code >= 0){
            printf("Success! Created process with PID %i.\n", error_code);
            return;
        }
        else if(error_code == -1){
            printf("Error: Could not create process.\n");
            return;
        }
        else{
            printf("Error Code %i: Unknown/specified error.\n", error_code);
            return;
        }
    }

    /* PRIORITY_P(priority{, PID}) */
    else if(strcmp(arg[0], "priority_p") == 0){
        if(argc != 2 && argc != 3){
            printf("Usage: priority_p, priority, {PID}\n");
            return;
        }
        if(argc == 2){
            if(queues[2].head == 0){
                printf("Error: There is no running process.\n");
                return;
            }
            pcb = queues[2].head;
            pcb->regs[0] = atoi(arg[1]);
            pid = pcb->pid;
        }
        else if(argc == 3){
            pcb = searchAll(atoi(arg[2]));
            if(pcb == 0){
				printf("Error: The specified PID does not exist.\n");
				return;
			}
			else{
				pcb->regs[0] = atoi(arg[1]);
			}
            pid = pcb->pid;
		}
        priority_p();

        if(error_code >= 0){
            printf("Success! Priority is now %i.\n", error_code);
        }
        else if(error_code == -2){
            printf("Error: Priority is out of range.\n");
        }
        else{
            printf("Error: Could not upgrade priority.\n");
        }
    }
    
    /* EXIT_P */
    else if(strcmp(arg[0], "exit_p") == 0){
        if(argc != 1 && argc != 2){
            printf("Usage: exit_p, {PID}\n");
            return;
        }
        pid = -1;
        if(argc == 2){
            pid = atoi(arg[1]);
        }
        exit_p();

        if(error_code >= 0){
            printf("Success! Process terminated.\n");
        }
        else if(error_code == -1){
            printf("Error: The specified PID does not exist.\n");
        }
		else if(error_code == -2){
            printf("Error: There is running process.\n");
        }
        else{
            printf("Error: Could not terminate process.\n");
        }
    }

    /* STATUS_P */
    else if(strcmp(arg[0], "status_p") == 0){
        if(argc != 3){
            printf("Usage: status_p, PID, &statusinfo\n");
            return;
        }
        if(atoi(arg[2]) < 0 || atoi(arg[2]) >= 10){
            printf("Error: Invalid address for status info.\n");
            return;
        }
        pcb = searchAll(atoi(arg[1]));
        if(pcb == 0){
            printf("Error: The specified PID does not exist.\n");
            return;
        }
        pid = pcb->pid;
        pcb->regs[0] = atoi(arg[2]);
        status_p();

        if(error_code >= 0){
            printf("Success! Status is %i at address %X.\n", death[atoi(arg[2])], &death[atoi(arg[2])]);
        }
        else{
            printf("Error: Could not get status.\n");
        }
    }

    /* OPEN_I */
    else if(strcmp(arg[0], "open_i") == 0){
        if(argc != 2){
            printf("Usage: open_i, PID\n");
            return;
        }
        if(atoi(arg[1]) < 0 || atoi(arg[1]) >= 100){
            printf("Error: PID out of range.\n");
            return;
        }
        pid = atoi(arg[1]);
        open_i();

        if(error_code >= 0){
            printf("Success! Mailbox ID is %i.\n", error_code);
        }
        else if(error_code == -2){
            printf("Error: The specified PID doesn't exist.\n");
        }
        else if(error_code == -3){
            printf("Error: This process already has a mailbox.\n");
        }
        else if(error_code == -1){
            printf("Error: All mailboxes are occupied.\n");
        }
		else{
            printf("Error: Cannot create mailboxes.\n");
        }
    }

    /* SEND_I */
    else if(strcmp(arg[0], "send_i") == 0){
        if(argc != 4){
            printf("Usage: send_i, MID, blocking_code, message\n");
            return;
        }
        if(queues[2].head == 0){
            printf("Error: There is no running process!\n");
            return;
        }
        pcb = queues[2].head;
        if(atoi(arg[1]) < 0 || atoi(arg[1]) >= 9){
            printf("Error: MID out of range.\n");
            return;
        }
		if(atoi(arg[2]) != 0 && atoi(arg[2]) != 1){
            printf("Error: Invalid blocking code.\n");
            return;
        }
        if(mailboxes[atoi(arg[1])].taken == -1){
            printf("Error: Invalid mailbox.\n");
            return;
        }

        pid = pcb->pid;
        pcb->regs[0] = atoi(arg[1]);
        pcb->regs[1] = atoi(arg[2]);
        pcb->regs[2] = atoi(arg[3]);

        send_i();

        if(error_code >= 0){
            printf("Success! Message sent to mailbox %i.\n", atoi(arg[1]));
        }
        else if(error_code == -1){
            printf("Error: Source or destination mailbox invalid.\n");
        }
        else if(error_code == -2){
            printf("Error: Message queue is full.\n");
        }
        else if(error_code == -3){
            printf("Error: The process associated with this mailbox no longer exists.\n");
        }
        else if(error_code == -4){
            printf("Error: The currently running process does not have a mailbox.\n");
        }
        else{
            printf("Error: Could not send message.\n");
        }
    }

    /* RECV_I */
    else if(strcmp(arg[0], "recv_i") == 0){
        if(argc != 4){
            printf("Usage: recv_i, MID, blocking_code, &msgbuf\n");
            return;
        }
        if(atoi(arg[1]) < 0 || atoi(arg[1]) >= 10){
            printf("Error: MID is out of range.\n");
            return;
        }
        if(atoi(arg[2]) != 0 && atoi(arg[2]) != 1){
            printf("Error: Invalid blocking code.\n");
            return;
        }
        if(atoi(arg[3]) < 0 || atoi(arg[3]) >= 10){
            printf("Error: Invalid address for msgbuf.\n");
            return;
        }
        if(mailboxes[atoi(arg[1])].taken == -1){
            printf("Error: Invalid mailbox!\n");
            return;
        }
        pcb = queues[2].head;
		if(pcb != NULL && pcb->pid == mailboxes[atoi(arg[1])].taken){
			pid = pcb->pid;
			pcb->regs[0] = atoi(arg[1]);
			pcb->regs[1] = atoi(arg[2]);
			pcb->regs[2] = atoi(arg[3]);
			recv_i();
		}
		else{
			error_code = -3;
		}

        if(error_code == 0){
            printf("Success! Message recieved: %i\n", death[atoi(arg[3])]);
            return;
        }
        if(error_code == -2){
            printf("Error: Mailbox empty.\n");
            return;
        }
        else if(error_code == -3){
            printf("Error: There is no running process, or the mailbox is not owned by the current running process!\n");
            return;
        }
		else{
            printf("Error: Cannot receive messages!\n");
            return;
        }

    }

    /* STATUS_I */
    else if(strcmp(arg[0], "status_i") == 0){
        if(argc != 3){
            printf("Usage: status_i, MID, &statusinfo\n");
            return;
        }
        if(atoi(arg[1]) < 0 || atoi(arg[1]) >= 10){
            printf("Error: MID is out of range.\n");
            return;
        }
        if(atoi(arg[2]) < 0 || atoi(arg[2]) >= 10){
            printf("Error: Invalid address for status info.\n");
            return;
        }
        if(mailboxes[atoi(arg[1])].taken == -1){
            printf("Error: Invalid mailbox!\n");
            return;
        }
        pcb = searchAll(mailboxes[atoi(arg[1])].taken);
        pid = pcb->pid;
        pcb->regs[0] = atoi(arg[1]);
        pcb->regs[1] = atoi(arg[2]);
        status_i();

        if(error_code >= 0){
            printf("Success! Status is %i at address %X.\n", death[atoi(arg[2])], &death[atoi(arg[2])]);
        }
        else{
            printf("Error: Could not get status.\n");
        }
    }

    /* ALLOC_M */
    else if(strcmp(arg[0], "alloc_m") == 0){
        int index;
        if(argc != 2){
            printf("Usage: alloc_m {size}\n");
            return;
        }
        
        if(queues[2].head == 0){
            printf("Error: There is no running process.\n");
            return;
        }

        index = alloc_m((int)queues[2].head->page_table, (int)atoi(arg[1]));
        if(index != -1){
            printf("Allocated %i bytes starting at logical address %i.\n", atoi(arg[1]), index);
            return;
        }
        else{
            printf("Error: Not enough memory.\n");
        }
    }

    /* DEALLOC_M */
    else if(strcmp(arg[0], "dealloc_m") == 0){
        if(argc != 2){
            printf("Usage: dealloc_m {Logical Address}\n");
            return;
        }
        
        if(queues[2].head == 0){
            printf("Error: There is no running process.\n");
            return;
        }

        dealloc_m((int)queues[2].head->page_table, (int)atoi(arg[1]));
        printf("Deallocated block starting at logical address %i.\n", atoi(arg[1]));
    }

    /* Unimplemented stuff */
    else if(strcmp(arg[0], "open_f") == 0){
        printf("This command has not yet been implemented.\n");
    }
    else if(strcmp(arg[0], "read_f") == 0){
        printf("This command has not yet been implemented.\n");
    }
    else if(strcmp(arg[0], "write_f") == 0){
        printf("This command has not yet been implemented.\n");
    }
    else if(strcmp(arg[0], "close_f") == 0){
        printf("This command has not yet been implemented.\n");
    }
    else if(strcmp(arg[0], "status_f") == 0){
        printf("This command has not yet been implemented.\n");
    }

    /* UNKNOWN COMMAND */
    else{
        printf("Unknown command %s.\n", arg[0]);
    }
}


void toggleRedirection(){
    if(argc == 1){
        if(strcmp(arg[0], "on") == 0){
            redirection = 1;
            printf("Redirection mode enabled.\n");
        }
        else if(strcmp(arg[0], "off") == 0){
            printf("Redirection mode disabled.\n");
            redirection = 0;
        }
        else{
            printf("Usage: redirection {on | off}\n");
        }
    }
    else{
        printf("Usage: redirection {on | off}\n");
    }
}

void logicalDump(int pid){
    int i = 0;
    process_control_block* pcb = searchAll(pid);

    printf("0\t");

    while(get_physical_address(pcb->page_table, i) != -1){
        printf("%i, ", read_from_virtual(pcb->page_table, i));
        if((i+1)%20 == 0){
            printf("\n%i\t", i+1);
        }
        i++;
    }

}

void listLRU(){
    Linked_List_Node* node = head;
	int i = 0;

	printf("\n\nLRU STACK:\t");
	if(numNodes != 0){
		while(i < numNodes - 1){
			printf("%i, ", node->value+56);
			if(node->value+56 < 100){
				printf(" ");
			}
			if((i+1)%6 == 0){
				printf("\n\t\t");
			}
			node = node->next;
			i++;
		}
		printf("%i\n", node->value+56);
	}
	else{
		printf("Empty\n");
	}
    printf("\n");

}


void printQueue(int qid){
    process_control_block* pcb = queues[qid].head;
    if(pcb == NULL){
        printf("Empty\n");
    }
    while(pcb != NULL){
        if(pcb == queues[qid].tail){
            printProcess(pcb);
            printf("\n");
        }
        else{
            printProcess(pcb);
            printf(", ");
        }
        pcb = pcb->next;
    }
}

void printProcess(process_control_block* pcb){
    if(scheduler_algorithm == 0){
        printf("{%i, %i}", pcb->pid, pcb->gid);
    }
    else if(scheduler_algorithm == 1){
        printf("{%i, %i, %i}", pcb->pid, pcb->gid, pcb->age);
    }
}




void printQueues(){
    printf("\nNew:\t\t");
    printQueue(0);
    printf("Ready:\t\t");
    printQueue(1);
    printf("Running:\t");
    printQueue(2);
    printf("Waiting:\t");
    printQueue(3);
    printf("Terminated:\t");
    printQueue(4);
    printf("\n");
}
